#pragma once

template<typename T, size_t D>
struct Matrix
{
public:
    
    Matrix();

    inline void set(size_t i, size_t j, T value);

    inline void set(T value);

    inline T get(size_t i, size_t j) const;

    inline T& get(size_t i, size_t j);

    inline void swapRows(size_t i, size_t j);

    inline void swapColumns(size_t i, size_t j);

    inline T determinant();

private:
    T m_data[D][D];
};

template<typename T, size_t D>
Matrix<T, D>::Matrix()
{
    set(0);
}

template<typename T, size_t D>
void Matrix<T, D>::set(size_t i, size_t j, T value)
{
    assert(i < D && j < D);
    m_data[i][j] = value;
}

template<typename T, size_t D>
void Matrix<T, D>::set(T value)
{
    for (size_t i = 0; i < D; ++i)
    {
        for (size_t j = 0; j < D; ++j)
        {
            set(i, j, value);
        }
    }
}

template<typename T, size_t D>
T Matrix<T, D>::get(size_t i, size_t j) const
{
    assert(i < D && j < D);
    
    return m_data[i][j];
}

template<typename T, size_t D>
T& Matrix<T, D>::get(size_t i, size_t j)
{
    assert(i < D && j < D);
    
    return m_data[i][j];
}

template<typename T, size_t D>
void Matrix<T, D>::swapRows(size_t iLhs, size_t iRhs)
{
    assert(iLhs < D && iRhs < D);
    
    if (iLhs == iRhs) return;

    for (int j = 0; j < D; ++j)
    {
        std::swap(m_data[iLhs][j], m_data[iRhs][j]);
    }
}

template<typename T, size_t D>
void Matrix<T, D>::swapColumns(size_t jLhs, size_t jRhs)
{
    assert(jLhs < D && jRhs < D);
    
    if (jLhs == jRhs) return;

    for (int i = 0; i < D; ++i)
    {
        std::swap(m_data[i][jLhs], m_data[i][jRhs]);
    }
}

template<typename T, size_t D>
T Matrix<T, D>::determinant()
{
    T result;
    return result;
}